/* TestClassLoader.js
 *
 * Copyright 2006, Tim Dwelle.
 *
 * Licensed under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of
 * the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in
 * writing, software distributed under the License is
 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied.  See
 * the License for the specific language governing
 * permissions and limitations under the License.
 *
 */

$package("dowry.test");

$import("dowry.loader.ClassLoader");

/**
 * TestClassLoader extends the base classloader to
 * perform dynamic classloading relative to the current
 * page and classpath, via an XHR request.  This classloader
 * is intended for running test cases.
 *
 * <p>
 * This classloader uses a classpath mechanism to find
 * classes relative to your current URL.  For example,
 * suppose that (a) your current URL is <code>
 * file://c:/myapp/test.html</code>, (b) the classpath
 * is <code>["js/","lib/","test/js/"]</code>, and (c) the
 * class to be loaded is <code>com.example.geom.Circle
 * </code>.  In that case, the following URLs will be
 * searched in order, until the class is found:
 * </p>
 *
 * <pre>
 * file://c:/myapp/js/com/example/geom/Circle.js
 * file://c:/myapp/lib/com/example/geom/Circle.js
 * file://c:/myapp/test/js/com/example/geom/Circle.js
 * </pre>
 *
 */
$class("TestClassLoader").$extends("ClassLoader").$as(
{
    /**
     * The classpath, as an array, to use for searching
     * for class defintions.  Each classpath node should
     * be a string representing a relative URL fragment.
     *
     */
    _classpath : null,

    /**
     * Creates a new classloader instance.
     *
     * @param parentClassLoader  the parent classloader
     *
     *
     * @param classpath          array of local paths to
     *                           prefix the fully-qualified
     *                           classname with (if no
     *                           classpath is specified, the
     *                           path of '.' is assumed)
     *
     */
    TestClassLoader : function(parentClassLoader, classpath)
    {
        this.$super(arguments);

        if (!classpath)
        {
            classpath = ['.'];
        }

        this._classpath = classpath;
    },

    /**
     * Finds the class with the specified fully-qualfiied
     * class name, by dynamically loading the class from
     * the classpath.
     *
     * @param className   the fully-qualified class name to
     *                    load
     *
     * @return            the class; or null, if not found
     *
     */
    _findClass : function(className)
    {
        var c = null;

        if (className)
        {
            var js = this._loadJs(className);
            c = this._parseClassDefinition(className, js[0], js[1]);
        }

        return c;
    },

    /**
     * Do the actual dynamic loading of the specified class
     * definition.
     *
     * TODO: add support for loading localization
     *       "patches" ?
     *
     * TODO: can we use DWR to handle the XHR stuff, so
     *       we don't need to worry about that stuff in
     *       our code?
     *
     * @param className   the fully-qualified class name to
     *                    load
     *
     * @return            the raw Javascript containing the
     *                    class definition, as a string
     *
     */
    _loadJs : function(className)
    {
        var js = null;
        var url = null;

        if (className)
        {
            var classNodes = className.split(".");

            var loc = document.location.href.split("/");
            loc.pop();

            for (var i=0; !js && i < this._classpath.length; i++)
            {
                var xhr = null;
                var path = this._classpath[i].split("/");

                url = loc.concat(path)
                         .concat(classNodes)
                         .join("/") + ".js";

                try
                {
                    if (window.XMLHttpRequest)
                    {
                        xhr = new XMLHttpRequest();
                    }
                    else if (window.ActiveXObject)
                    {
                        xhr = new ActiveXObject("Microsoft.XMLHTTP");
                    }

                    if (xhr)
                    {
                        xhr.open("GET", url, false);
                        xhr.send("");
                        js = xhr.responseText;
                    }
                }
                catch(e)
                {
                    // ignore... try next url in classpath
                }
            }
        }

        return [ js, url ];
    }
});